require "teaspoon/formatter/base"

module Teaspoon
  module Formatter
    class Teamcity < Base
      def initialize(*args)
        log("enteredTheMatrix timestamp='#{Time.now.to_json.gsub('"', '')}'")
        super
      end

      protected

      def log_runner(result)
        log("testCount count='#{result.total}' timestamp='#{result.start}'")
      end

      def log_suite(result)
        log_end_suite
        log("testSuiteStarted name='#{result.label}'")
      end

      def log_passing_spec(result)
        log_teamcity_spec(type: "testStarted", desc: escape(result.description))
      end

      def log_pending_spec(result)
        log_teamcity_spec(type: "testIgnored", desc: escape(result.description))
      end

      def log_failing_spec(result)
        log_teamcity_spec(type: "testStarted", desc: escape(result.description)) do
          log("testFailed name='#{escape(result.description)}' message='#{escape(result.message)}'")
        end
      end

      def log_error(result)
        log("message text='#{escape(result.message)}' errorDetails='#{escape_trace(result.trace)}' status='ERROR'")
      end

      def log_result(result)
        log_end_suite
        @result = result
      end

      def log_coverage(message)
        log("testSuiteStarted name='Coverage summary'")
        log_line(message)
        log("testSuiteFinished name='Coverage summary'")
      end

      def log_threshold_failure(message)
        log("testSuiteStarted name='Coverage thresholds'")
        log_teamcity_spec(type: "testStarted", desc: "Coverage thresholds") do
          log("testFailed name='Coverage thresholds' message='were not met'")
          log_line(message)
        end
        log("testSuiteFinished name='Coverage thresholds'")
      end

      def log_complete(failure_count)
        log_line("\nFinished in #{@result.elapsed} seconds")
        stats = "#{pluralize('example', run_count)}, #{pluralize('failure', failure_count)}"
        stats << ", #{pendings.size} pending" if pendings.size > 0
        log_line(stats)
        log_line if failure_count > 0
      end

      private

      def log_end_suite
        log("testSuiteFinished name='#{escape(@last_suite.label)}'") if @last_suite
      end

      def log_teamcity_spec(opts, &_block)
        log("#{opts[:type]} name='#{opts[:desc]}' captureStandardOutput='true'")
        log_line(@stdout.gsub(/\n$/, "")) unless @stdout.blank?
        yield if block_given?
        log("testFinished name='#{opts[:desc]}'")
      end

      def log(str)
        log_line("##teamcity[#{str}]")
      end

      def escape(str)
        str = str.gsub(/[|'\[\]]/) { |c| "|#{c}" }
        str.gsub("\n", "|n").gsub("\r", "|r")
      end

      def escape_trace(trace)
        lines = trace.map { |t| ["#{t['file']}:#{t['line']}", t["function"]].compact.join(" ") }
        escape(lines.join("\n"))
      end
    end
  end
end
